(function (root, factory, undef) {
    if (typeof exports === 'object') {
        // CommonJS
        module.exports = exports = factory(require('./core'), require('./evpkdf'));
    } else {
        if (typeof define === 'function' && define.amd) {
            // AMD
            define(['./core', './evpkdf'], factory);
        } else {
            // Global (browser)
            factory(root.CryptoJS);
        }
    }
})(this, function (CryptoJS) {
    /**
     * Cipher core components.
     */
    CryptoJS.lib.Cipher ||
        (function (undefined) {
            // Shortcuts
            var C = CryptoJS;
            var C_lib = C.lib;
            var Base = C_lib.Base;
            var WordArray = C_lib.WordArray;
            var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm;
            var C_enc = C.enc;
            var Utf8 = C_enc.Utf8;
            var Base64 = C_enc.Base64;
            var C_algo = C.algo;
            var EvpKDF = C_algo.EvpKDF;
            /**
             * Abstract base cipher template.
             *
             * @property {number} keySize This cipher's key size. Default: 4 (128 bits)
             * @property {number} ivSize This cipher's IV size. Default: 4 (128 bits)
             * @property {number} _ENC_XFORM_MODE A constant representing encryption mode.
             * @property {number} _DEC_XFORM_MODE A constant representing decryption mode.
             */

            var Cipher = (C_lib.Cipher = BufferedBlockAlgorithm.extend({
                /**
                 * Configuration options.
                 *
                 * @property {WordArray} iv The IV to use for this operation.
                 */
                cfg: Base.extend(),

                /**
                 * Creates this cipher in encryption mode.
                 *
                 * @param {WordArray} key The key.
                 * @param {Object} cfg (Optional) The configuration options to use for this operation.
                 *
                 * @return {Cipher} A cipher instance.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray });
                 */
                createEncryptor: function (key, cfg) {
                    return this.create(this._ENC_XFORM_MODE, key, cfg);
                },

                /**
                 * Creates this cipher in decryption mode.
                 *
                 * @param {WordArray} key The key.
                 * @param {Object} cfg (Optional) The configuration options to use for this operation.
                 *
                 * @return {Cipher} A cipher instance.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray });
                 */
                createDecryptor: function (key, cfg) {
                    return this.create(this._DEC_XFORM_MODE, key, cfg);
                },

                /**
                 * Initializes a newly created cipher.
                 *
                 * @param {number} xformMode Either the encryption or decryption transormation mode constant.
                 * @param {WordArray} key The key.
                 * @param {Object} cfg (Optional) The configuration options to use for this operation.
                 *
                 * @example
                 *
                 *     var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray });
                 */
                init: function (xformMode, key, cfg) {
                    // Apply config defaults
                    this.cfg = this.cfg.extend(cfg); // Store transform mode and key

                    this._xformMode = xformMode;
                    this._key = key; // Set initial values

                    this.reset();
                },

                /**
                 * Resets this cipher to its initial state.
                 *
                 * @example
                 *
                 *     cipher.reset();
                 */
                reset: function () {
                    // Reset data buffer
                    BufferedBlockAlgorithm.reset.call(this); // Perform concrete-cipher logic

                    this._doReset();
                },

                /**
                 * Adds data to be encrypted or decrypted.
                 *
                 * @param {WordArray|string} dataUpdate The data to encrypt or decrypt.
                 *
                 * @return {WordArray} The data after processing.
                 *
                 * @example
                 *
                 *     var encrypted = cipher.process('data');
                 *     var encrypted = cipher.process(wordArray);
                 */
                process: function (dataUpdate) {
                    // Append
                    this._append(dataUpdate); // Process available blocks

                    return this._process();
                },

                /**
                 * Finalizes the encryption or decryption process.
                 * Note that the finalize operation is effectively a destructive, read-once operation.
                 *
                 * @param {WordArray|string} dataUpdate The final data to encrypt or decrypt.
                 *
                 * @return {WordArray} The data after final processing.
                 *
                 * @example
                 *
                 *     var encrypted = cipher.finalize();
                 *     var encrypted = cipher.finalize('data');
                 *     var encrypted = cipher.finalize(wordArray);
                 */
                finalize: function (dataUpdate) {
                    // Final data update
                    if (dataUpdate) {
                        this._append(dataUpdate);
                    } // Perform concrete-cipher logic

                    var finalProcessedData = this._doFinalize();

                    return finalProcessedData;
                },
                keySize: 128 / 32,
                ivSize: 128 / 32,
                _ENC_XFORM_MODE: 1,
                _DEC_XFORM_MODE: 2,

                /**
                 * Creates shortcut functions to a cipher's object interface.
                 *
                 * @param {Cipher} cipher The cipher to create a helper for.
                 *
                 * @return {Object} An object with encrypt and decrypt shortcut functions.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES);
                 */
                _createHelper: (function () {
                    function selectCipherStrategy(key) {
                        if (typeof key == 'string') {
                            return PasswordBasedCipher;
                        } else {
                            return SerializableCipher;
                        }
                    }

                    return function (cipher) {
                        return {
                            encrypt: function (message, key, cfg) {
                                return selectCipherStrategy(key).encrypt(cipher, message, key, cfg);
                            },
                            decrypt: function (ciphertext, key, cfg) {
                                return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg);
                            }
                        };
                    };
                })()
            }));
            /**
             * Abstract base stream cipher template.
             *
             * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits)
             */

            var StreamCipher = (C_lib.StreamCipher = Cipher.extend({
                _doFinalize: function () {
                    // Process partial blocks
                    var finalProcessedBlocks = this._process(!!'flush');

                    return finalProcessedBlocks;
                },
                blockSize: 1
            }));
            /**
             * Mode namespace.
             */

            var C_mode = (C.mode = {});
            /**
             * Abstract base block cipher mode template.
             */

            var BlockCipherMode = (C_lib.BlockCipherMode = Base.extend({
                /**
                 * Creates this mode for encryption.
                 *
                 * @param {Cipher} cipher A block cipher instance.
                 * @param {Array} iv The IV words.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words);
                 */
                createEncryptor: function (cipher, iv) {
                    return this.Encryptor.create(cipher, iv);
                },

                /**
                 * Creates this mode for decryption.
                 *
                 * @param {Cipher} cipher A block cipher instance.
                 * @param {Array} iv The IV words.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words);
                 */
                createDecryptor: function (cipher, iv) {
                    return this.Decryptor.create(cipher, iv);
                },

                /**
                 * Initializes a newly created mode.
                 *
                 * @param {Cipher} cipher A block cipher instance.
                 * @param {Array} iv The IV words.
                 *
                 * @example
                 *
                 *     var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words);
                 */
                init: function (cipher, iv) {
                    this._cipher = cipher;
                    this._iv = iv;
                }
            }));
            /**
             * Cipher Block Chaining mode.
             */

            var CBC = (C_mode.CBC = (function () {
                /**
                 * Abstract base CBC mode.
                 */
                var CBC = BlockCipherMode.extend();
                /**
                 * CBC encryptor.
                 */

                CBC.Encryptor = CBC.extend({
                    /**
                     * Processes the data block at offset.
                     *
                     * @param {Array} words The data words to operate on.
                     * @param {number} offset The offset where the block starts.
                     *
                     * @example
                     *
                     *     mode.processBlock(data.words, offset);
                     */
                    processBlock: function (words, offset) {
                        // Shortcuts
                        var cipher = this._cipher;
                        var blockSize = cipher.blockSize; // XOR and encrypt

                        xorBlock.call(this, words, offset, blockSize);
                        cipher.encryptBlock(words, offset); // Remember this block to use with next block

                        this._prevBlock = words.slice(offset, offset + blockSize);
                    }
                });
                /**
                 * CBC decryptor.
                 */

                CBC.Decryptor = CBC.extend({
                    /**
                     * Processes the data block at offset.
                     *
                     * @param {Array} words The data words to operate on.
                     * @param {number} offset The offset where the block starts.
                     *
                     * @example
                     *
                     *     mode.processBlock(data.words, offset);
                     */
                    processBlock: function (words, offset) {
                        // Shortcuts
                        var cipher = this._cipher;
                        var blockSize = cipher.blockSize; // Remember this block to use with next block

                        var thisBlock = words.slice(offset, offset + blockSize); // Decrypt and XOR

                        cipher.decryptBlock(words, offset);
                        xorBlock.call(this, words, offset, blockSize); // This block becomes the previous block

                        this._prevBlock = thisBlock;
                    }
                });

                function xorBlock(words, offset, blockSize) {
                    var block; // Shortcut

                    var iv = this._iv; // Choose mixing block

                    if (iv) {
                        block = iv; // Remove IV for subsequent blocks

                        this._iv = undefined;
                    } else {
                        block = this._prevBlock;
                    } // XOR blocks

                    for (var i = 0; i < blockSize; i++) {
                        words[offset + i] ^= block[i];
                    }
                }

                return CBC;
            })());
            /**
             * Padding namespace.
             */

            var C_pad = (C.pad = {});
            /**
             * PKCS #5/7 padding strategy.
             */

            var Pkcs7 = (C_pad.Pkcs7 = {
                /**
                 * Pads data using the algorithm defined in PKCS #5/7.
                 *
                 * @param {WordArray} data The data to pad.
                 * @param {number} blockSize The multiple that the data should be padded to.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     CryptoJS.pad.Pkcs7.pad(wordArray, 4);
                 */
                pad: function (data, blockSize) {
                    // Shortcut
                    var blockSizeBytes = blockSize * 4; // Count padding bytes

                    var nPaddingBytes = blockSizeBytes - (data.sigBytes % blockSizeBytes); // Create padding word

                    var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes; // Create padding

                    var paddingWords = [];

                    for (var i = 0; i < nPaddingBytes; i += 4) {
                        paddingWords.push(paddingWord);
                    }

                    var padding = WordArray.create(paddingWords, nPaddingBytes); // Add padding

                    data.concat(padding);
                },

                /**
                 * Unpads data that had been padded using the algorithm defined in PKCS #5/7.
                 *
                 * @param {WordArray} data The data to unpad.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     CryptoJS.pad.Pkcs7.unpad(wordArray);
                 */
                unpad: function (data) {
                    // Get number of padding bytes from last byte
                    var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 255; // Remove padding

                    data.sigBytes -= nPaddingBytes;
                }
            });
            /**
             * Abstract base block cipher template.
             *
             * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits)
             */

            var BlockCipher = (C_lib.BlockCipher = Cipher.extend({
                /**
                 * Configuration options.
                 *
                 * @property {Mode} mode The block mode to use. Default: CBC
                 * @property {Padding} padding The padding strategy to use. Default: Pkcs7
                 */
                cfg: Cipher.cfg.extend({
                    mode: CBC,
                    padding: Pkcs7
                }),
                reset: function () {
                    var modeCreator; // Reset cipher

                    Cipher.reset.call(this); // Shortcuts

                    var cfg = this.cfg;
                    var iv = cfg.iv;
                    var mode = cfg.mode; // Reset block mode

                    if (this._xformMode == this._ENC_XFORM_MODE) {
                        modeCreator = mode.createEncryptor;
                    } else {
                    /* if (this._xformMode == this._DEC_XFORM_MODE) */
                        modeCreator = mode.createDecryptor; // Keep at least one block in the buffer for unpadding

                        this._minBufferSize = 1;
                    }

                    if (this._mode && this._mode.__creator == modeCreator) {
                        this._mode.init(this, iv && iv.words);
                    } else {
                        this._mode = modeCreator.call(mode, this, iv && iv.words);
                        this._mode.__creator = modeCreator;
                    }
                },
                _doProcessBlock: function (words, offset) {
                    this._mode.processBlock(words, offset);
                },
                _doFinalize: function () {
                    var finalProcessedBlocks; // Shortcut

                    var padding = this.cfg.padding; // Finalize

                    if (this._xformMode == this._ENC_XFORM_MODE) {
                        // Pad data
                        padding.pad(this._data, this.blockSize); // Process final blocks

                        finalProcessedBlocks = this._process(!!'flush');
                    } else {
                    /* if (this._xformMode == this._DEC_XFORM_MODE) */
                        // Process final blocks
                        finalProcessedBlocks = this._process(!!'flush'); // Unpad data

                        padding.unpad(finalProcessedBlocks);
                    }

                    return finalProcessedBlocks;
                },
                blockSize: 128 / 32
            }));
            /**
             * A collection of cipher parameters.
             *
             * @property {WordArray} ciphertext The raw ciphertext.
             * @property {WordArray} key The key to this ciphertext.
             * @property {WordArray} iv The IV used in the ciphering operation.
             * @property {WordArray} salt The salt used with a key derivation function.
             * @property {Cipher} algorithm The cipher algorithm.
             * @property {Mode} mode The block mode used in the ciphering operation.
             * @property {Padding} padding The padding scheme used in the ciphering operation.
             * @property {number} blockSize The block size of the cipher.
             * @property {Format} formatter The default formatting strategy to convert this cipher params object to a string.
             */

            var CipherParams = (C_lib.CipherParams = Base.extend({
                /**
                 * Initializes a newly created cipher params object.
                 *
                 * @param {Object} cipherParams An object with any of the possible cipher parameters.
                 *
                 * @example
                 *
                 *     var cipherParams = CryptoJS.lib.CipherParams.create({
                 *         ciphertext: ciphertextWordArray,
                 *         key: keyWordArray,
                 *         iv: ivWordArray,
                 *         salt: saltWordArray,
                 *         algorithm: CryptoJS.algo.AES,
                 *         mode: CryptoJS.mode.CBC,
                 *         padding: CryptoJS.pad.PKCS7,
                 *         blockSize: 4,
                 *         formatter: CryptoJS.format.OpenSSL
                 *     });
                 */
                init: function (cipherParams) {
                    this.mixIn(cipherParams);
                },

                /**
                 * Converts this cipher params object to a string.
                 *
                 * @param {Format} formatter (Optional) The formatting strategy to use.
                 *
                 * @return {string} The stringified cipher params.
                 *
                 * @throws Error If neither the formatter nor the default formatter is set.
                 *
                 * @example
                 *
                 *     var string = cipherParams + '';
                 *     var string = cipherParams.toString();
                 *     var string = cipherParams.toString(CryptoJS.format.OpenSSL);
                 */
                toString: function (formatter) {
                    return (formatter || this.formatter).stringify(this);
                }
            }));
            /**
             * Format namespace.
             */

            var C_format = (C.format = {});
            /**
             * OpenSSL formatting strategy.
             */

            var OpenSSLFormatter = (C_format.OpenSSL = {
                /**
                 * Converts a cipher params object to an OpenSSL-compatible string.
                 *
                 * @param {CipherParams} cipherParams The cipher params object.
                 *
                 * @return {string} The OpenSSL-compatible string.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams);
                 */
                stringify: function (cipherParams) {
                    var wordArray; // Shortcuts

                    var ciphertext = cipherParams.ciphertext;
                    var salt = cipherParams.salt; // Format

                    if (salt) {
                        wordArray = WordArray.create([1398893684, 1701076831]).concat(salt).concat(ciphertext);
                    } else {
                        wordArray = ciphertext;
                    }

                    return wordArray.toString(Base64);
                },

                /**
                 * Converts an OpenSSL-compatible string to a cipher params object.
                 *
                 * @param {string} openSSLStr The OpenSSL-compatible string.
                 *
                 * @return {CipherParams} The cipher params object.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString);
                 */
                parse: function (openSSLStr) {
                    var salt; // Parse base64

                    var ciphertext = Base64.parse(openSSLStr); // Shortcut

                    var ciphertextWords = ciphertext.words; // Test for salt

                    if (ciphertextWords[0] == 1398893684 && ciphertextWords[1] == 1701076831) {
                        // Extract salt
                        salt = WordArray.create(ciphertextWords.slice(2, 4)); // Remove salt from ciphertext

                        ciphertextWords.splice(0, 4);
                        ciphertext.sigBytes -= 16;
                    }

                    return CipherParams.create({
                        ciphertext: ciphertext,
                        salt: salt
                    });
                }
            });
            /**
             * A cipher wrapper that returns ciphertext as a serializable cipher params object.
             */

            var SerializableCipher = (C_lib.SerializableCipher = Base.extend({
                /**
                 * Configuration options.
                 *
                 * @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL
                 */
                cfg: Base.extend({
                    format: OpenSSLFormatter
                }),

                /**
                 * Encrypts a message.
                 *
                 * @param {Cipher} cipher The cipher algorithm to use.
                 * @param {WordArray|string} message The message to encrypt.
                 * @param {WordArray} key The key.
                 * @param {Object} cfg (Optional) The configuration options to use for this operation.
                 *
                 * @return {CipherParams} A cipher params object.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key);
                 *     var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv });
                 *     var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL });
                 */
                encrypt: function (cipher, message, key, cfg) {
                    // Apply config defaults
                    cfg = this.cfg.extend(cfg); // Encrypt

                    var encryptor = cipher.createEncryptor(key, cfg);
                    var ciphertext = encryptor.finalize(message); // Shortcut

                    var cipherCfg = encryptor.cfg; // Create and return serializable cipher params

                    return CipherParams.create({
                        ciphertext: ciphertext,
                        key: key,
                        iv: cipherCfg.iv,
                        algorithm: cipher,
                        mode: cipherCfg.mode,
                        padding: cipherCfg.padding,
                        blockSize: cipher.blockSize,
                        formatter: cfg.format
                    });
                },

                /**
                 * Decrypts serialized ciphertext.
                 *
                 * @param {Cipher} cipher The cipher algorithm to use.
                 * @param {CipherParams|string} ciphertext The ciphertext to decrypt.
                 * @param {WordArray} key The key.
                 * @param {Object} cfg (Optional) The configuration options to use for this operation.
                 *
                 * @return {WordArray} The plaintext.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL });
                 *     var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL });
                 */
                decrypt: function (cipher, ciphertext, key, cfg) {
                    // Apply config defaults
                    cfg = this.cfg.extend(cfg); // Convert string to CipherParams

                    ciphertext = this._parse(ciphertext, cfg.format); // Decrypt

                    var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext);
                    return plaintext;
                },

                /**
                 * Converts serialized ciphertext to CipherParams,
                 * else assumed CipherParams already and returns ciphertext unchanged.
                 *
                 * @param {CipherParams|string} ciphertext The ciphertext.
                 * @param {Formatter} format The formatting strategy to use to parse serialized ciphertext.
                 *
                 * @return {CipherParams} The unserialized ciphertext.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format);
                 */
                _parse: function (ciphertext, format) {
                    if (typeof ciphertext == 'string') {
                        return format.parse(ciphertext, this);
                    } else {
                        return ciphertext;
                    }
                }
            }));
            /**
             * Key derivation function namespace.
             */

            var C_kdf = (C.kdf = {});
            /**
             * OpenSSL key derivation function.
             */

            var OpenSSLKdf = (C_kdf.OpenSSL = {
                /**
                 * Derives a key and IV from a password.
                 *
                 * @param {string} password The password to derive from.
                 * @param {number} keySize The size in words of the key to generate.
                 * @param {number} ivSize The size in words of the IV to generate.
                 * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly.
                 *
                 * @return {CipherParams} A cipher params object with the key, IV, and salt.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
                 *     var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
                 */
                execute: function (password, keySize, ivSize, salt) {
                    // Generate random salt
                    if (!salt) {
                        salt = WordArray.random(64 / 8);
                    } // Derive key and IV

                    var key = EvpKDF.create({
                        keySize: keySize + ivSize
                    }).compute(password, salt); // Separate key and IV

                    var iv = WordArray.create(key.words.slice(keySize), ivSize * 4);
                    key.sigBytes = keySize * 4; // Return params

                    return CipherParams.create({
                        key: key,
                        iv: iv,
                        salt: salt
                    });
                }
            });
            /**
             * A serializable cipher wrapper that derives the key from a password,
             * and returns ciphertext as a serializable cipher params object.
             */

            var PasswordBasedCipher = (C_lib.PasswordBasedCipher = SerializableCipher.extend({
                /**
                 * Configuration options.
                 *
                 * @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL
                 */
                cfg: SerializableCipher.cfg.extend({
                    kdf: OpenSSLKdf
                }),

                /**
                 * Encrypts a message using a password.
                 *
                 * @param {Cipher} cipher The cipher algorithm to use.
                 * @param {WordArray|string} message The message to encrypt.
                 * @param {string} password The password.
                 * @param {Object} cfg (Optional) The configuration options to use for this operation.
                 *
                 * @return {CipherParams} A cipher params object.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password');
                 *     var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL });
                 */
                encrypt: function (cipher, message, password, cfg) {
                    // Apply config defaults
                    cfg = this.cfg.extend(cfg); // Derive key and other params

                    var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize); // Add IV to config

                    cfg.iv = derivedParams.iv; // Encrypt

                    var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg); // Mix in derived params

                    ciphertext.mixIn(derivedParams);
                    return ciphertext;
                },

                /**
                 * Decrypts serialized ciphertext using a password.
                 *
                 * @param {Cipher} cipher The cipher algorithm to use.
                 * @param {CipherParams|string} ciphertext The ciphertext to decrypt.
                 * @param {string} password The password.
                 * @param {Object} cfg (Optional) The configuration options to use for this operation.
                 *
                 * @return {WordArray} The plaintext.
                 *
                 * @static
                 *
                 * @example
                 *
                 *     var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL });
                 *     var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL });
                 */
                decrypt: function (cipher, ciphertext, password, cfg) {
                    // Apply config defaults
                    cfg = this.cfg.extend(cfg); // Convert string to CipherParams

                    ciphertext = this._parse(ciphertext, cfg.format); // Derive key and other params

                    var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt); // Add IV to config

                    cfg.iv = derivedParams.iv; // Decrypt

                    var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg);
                    return plaintext;
                }
            }));
        })();
});
